package com.heo.client.app;

import java.net.InetSocketAddress;
import java.util.Date;

import javax.swing.JTextArea;

import org.apache.log4j.Logger;

import com.heo.protocol.packet.BasePacket;
import com.heo.protocol.packet.DeviceCommand;
import com.heo.protocol.packet.ReportDeviceStatusReq;
import com.heo.protocol.packet.SetUploadLogResp;
import com.heo.protocol.packet.SocketPacket;
import com.heo.protocol.packet.StartDeviceReq;
import com.heo.protocol.packet.StartDeviceResp;
import com.heo.protocol.packet.UpdateFirmwareResp;
import com.heo.util.SequenceHelper;
import com.heo.util.TimeHelper;

import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.timeout.IdleStateEvent;

/**
 * Created by zhangcf
 */
public class AppClientHandler extends ChannelInboundHandlerAdapter
{
  private Logger log = Logger.getLogger(this.getClass());
  private static String lineWrap = "\r\n";

  private int index = 0;
  private JTextArea consoleArea;
  private DeviceWindow window;

  private short times = 0;

  private String now()
  {
    return TimeHelper.dateTimeToStr(new Date()) + " - ";
  }

  public AppClientHandler(int index, DeviceWindow window)
  {
    this.index = index;
    this.window = window;
    consoleArea = window.getConsoleArea();
  }

  public AppClientHandler()
  {

  }

  @Override
  public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception
  {
    log.error(cause.getMessage(), cause);
    super.exceptionCaught(ctx, cause);
    ctx.close();
  }

  /***
   * 通道连接成功时触发
   */
  @Override
  public void channelActive(ChannelHandlerContext ctx) throws Exception
  {
    String channelId = ctx.channel().id().asShortText();
    String localIpAddress = ((InetSocketAddress) ctx.channel().localAddress()).getAddress().getHostAddress();
    log.info("[client][" + index + "][" + channelId + "][" + localIpAddress + "]连接创建成功。");
    consoleArea.append(now() + "[client][" + index + "][" + channelId + "]连接创建成功。" + lineWrap);
    ClientSocketChannelMap.add(index, ctx.channel());
  }

  /**
   * 收到服务端发过来的数据时触发
   */
  @Override
  public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception
  {
    String channelId = ctx.channel().id().asShortText();
    String localIpAddress = ((InetSocketAddress) ctx.channel().localAddress()).getAddress().getHostAddress();

    log.debug("[client][" + channelId + "][" + localIpAddress + "]<---收到消息：[" + msg.toString() + "]");

    String logHeader = "[client][" + channelId + "][" + localIpAddress + "]";

    SocketPacket body = (SocketPacket) msg;

    int commandId = body.getCommandId();

    switch (commandId)
    {
      // 启动指令
      case DeviceCommand.START_DEVICE_REQ:
        log.debug("收到启动指令。");
        consoleArea.append(now() + "[client][" + index + "][" + channelId + "]收到启动指令。" + msg.toString() + lineWrap);
        StartDeviceReq startReq = new StartDeviceReq(logHeader, body);// 将数据包转换为请求包
        // 生成启动结果指令，从界面取到启动的结果
        StartDeviceResp startResp = new StartDeviceResp((short) body.getSequenceId());
        startResp.setChannelIndex(startReq.getChannelIndex());
        startResp.setStartResult((byte) window.getStartResult());
        sendPacket(index, ctx.channel(), startResp);
        consoleArea.append(now() + "[client][" + index + "][" + channelId + "]回复启动结果：" + startResp.toString() + lineWrap);
        boolean isAutoResponse = window.getAutoResponse();
        // 如果需要模拟工作过程，
        if (isAutoResponse)
        {
          ReportDeviceStatusReq rdsr = new ReportDeviceStatusReq();
          rdsr.setSendCount((byte) 0x01);
          rdsr.setSequenceId(SequenceHelper.getId(channelId));
          rdsr.setChannelIndex(Integer.valueOf(window.getChannelIndexField().getText()));
          rdsr.setChannelStatus((short) 0x01);
          log.debug(rdsr.toString());
          consoleArea.append(now() + "[client][" + index + "][" + channelId + "]开始工作模拟，3秒后将发送启动成功指令：" + rdsr.toString() + lineWrap);
          Thread.sleep(3000);
          sendPacket(index, ctx.channel(), rdsr);

          rdsr = new ReportDeviceStatusReq();
          rdsr.setSendCount((byte) 0x01);
          rdsr.setSequenceId(SequenceHelper.getId(channelId));
          rdsr.setChannelIndex(Integer.valueOf(window.getChannelIndexField().getText()));
          rdsr.setChannelStatus((short) 0x02);
          log.debug(rdsr.toString());
          consoleArea.append(now() + "[client][" + index + "][" + channelId + "]发送启动成功状态完成。3秒后将发送工作中状态：" + rdsr.toString() + lineWrap);
          Thread.sleep(3000);
          sendPacket(index, ctx.channel(), rdsr);

          rdsr = new ReportDeviceStatusReq();
          rdsr.setSendCount((byte) 0x01);
          rdsr.setSequenceId(SequenceHelper.getId(channelId));
          rdsr.setChannelIndex(Integer.valueOf(window.getChannelIndexField().getText()));
          rdsr.setChannelStatus((short) 0x03);
          log.debug(rdsr.toString());
          consoleArea.append(now() + "[client][" + index + "][" + channelId + "]发送工作中状态完成。30秒后将发送工作结束状态：" + rdsr.toString() + lineWrap);
          Thread.sleep(30000);
          sendPacket(index, ctx.channel(), rdsr);

          rdsr = new ReportDeviceStatusReq();
          rdsr.setSendCount((byte) 0x01);
          rdsr.setSequenceId(SequenceHelper.getId(channelId));
          rdsr.setChannelIndex(Integer.valueOf(window.getChannelIndexField().getText()));
          rdsr.setChannelStatus((short) 0x00);
          log.debug(rdsr.toString());
          consoleArea.append(now() + "[client][" + index + "][" + channelId + "]发送工作结束状态完成。3秒后将发送空闲状态：" + rdsr.toString() + lineWrap);
          Thread.sleep(3000);
          sendPacket(index, ctx.channel(), rdsr);

          consoleArea.append(now() + "[client][" + index + "][" + channelId + "]设备工作自动模拟结束。" + lineWrap);
        }
        break;

      // 登录结果
      case DeviceCommand.LOGIN_RESP:
        log.debug("收到登录结果。");
        consoleArea.append(now() + "[client][" + index + "][" + channelId + "]收到登录应答：" + msg.toString() + lineWrap);
        break;

      // 退出登录结果
      case DeviceCommand.LOGINOUT_RESP:
        log.debug("收到退出登录结果。");
        consoleArea.append(now() + "[client][" + index + "][" + channelId + "]收到退出登录应答：" + msg.toString() + lineWrap);
        ctx.channel().close();
        break;

      // 心跳结果
      case DeviceCommand.HEART_RESP:
        log.debug("收到心跳结果。");
        consoleArea.append(now() + "[client][" + index + "][" + channelId + "]收到心跳应答：" + msg.toString() + lineWrap);
        break;

      // 带信号的心跳结果
      case DeviceCommand.HEART_BY_SINGAL_RESP:
        log.debug("收到带信号的心跳结果。");
        consoleArea.append(now() + "[client][" + index + "][" + channelId + "]收到带信号强度的心跳应答：" + msg.toString() + lineWrap);
        break;

      // 修改密码结果
      case DeviceCommand.EDIT_LOGIN_PASSWORD_RESP:
        log.debug("收到修改密码结果。");
        consoleArea.append(now() + "[client][" + index + "][" + channelId + "]收到修改密码应答：" + msg.toString() + lineWrap);
        break;

      // 状态报告
      case DeviceCommand.REPORT_DEVICE_STATUS_RESP:
        log.debug("收到状态上报应答。");
        consoleArea.append(now() + "[client][" + index + "][" + channelId + "]收到状态上报应答：" + msg.toString() + lineWrap);
        break;

      // 更新设备参数
      case DeviceCommand.GET_DEVICE_PARA_RESP:
        log.debug("收到更新设备参数应答。");
        consoleArea.append(now() + "[client][" + index + "][" + channelId + "]收到更新设备参数应答：" + msg.toString() + lineWrap);
        break;

      // 上传日志
      case DeviceCommand.SET_UPLOAD_LOG_REQ:
        log.debug("收到上传日志请求。");
        consoleArea.append(now() + "[client][" + index + "][" + channelId + "]收到上传日志请求：" + msg.toString() + lineWrap);
        SetUploadLogResp sulr = new SetUploadLogResp((short) body.getSequenceId());
        sendPacket(index, ctx.channel(), sulr);
        consoleArea.append(now() + "[client][" + index + "][" + channelId + "]回复上传日志应答：" + sulr.toString() + lineWrap);
        break;

      // 上传日志
      case DeviceCommand.UPLOAD_LOG_RESP:
        log.debug("收到设备上传日志应答。");
        consoleArea.append(now() + "[client][" + index + "][" + channelId + "]收到设备上传日志应答：" + msg.toString() + lineWrap);
        break;

      // 请求升级固件
      case DeviceCommand.FIRMWARE_NOTICE_REQ:
        log.debug("收到固件升级请求。");
        consoleArea.append(now() + "[client][" + index + "][" + channelId + "]收到固件升级请求：" + msg.toString() + lineWrap);

        UpdateFirmwareResp ufr = new UpdateFirmwareResp((short) body.getSequenceId());
        sendPacket(index, ctx.channel(), ufr);
        consoleArea.append(now() + "[client][" + index + "][" + channelId + "]回复固件升级应答：" + ufr.toString() + lineWrap);
        break;

      // 收到下载的固件数据
      case DeviceCommand.FIRMWARE_DOWNLOAD_RESP:
        log.debug("收到固件内容。");
        consoleArea.append(now() + "[client][" + index + "][" + channelId + "]收到固件内容应答：" + msg.toString() + lineWrap);
        break;

      // 收到二维码数据
      case DeviceCommand.GET_DEVICE_CODE_RESP:
        log.debug("收到设备二维码内容。");
        consoleArea.append(now() + "[client][" + index + "][" + channelId + "]收到获取二维码应答：" + msg.toString() + lineWrap);
        break;

      // 收到反向扫码应答数据
      case DeviceCommand.READ_DEVICE_CODE_RESP:
        log.debug("收到反向扫码应答数据。");
        consoleArea.append(now() + "[client][" + index + "][" + channelId + "]收到反向扫码应答：" + msg.toString() + lineWrap);
        break;

      // 收到重启指令
      case DeviceCommand.REBOOT_DEVICE_REQ:
        log.debug("收到重启指令。");
        consoleArea.append(now() + "[client][" + index + "][" + channelId + "]收到重启指令：" + msg.toString() + lineWrap);
        break;

      default:
        break;
    }
  }

  /***
   * 通道断开时触发
   */
  @Override
  public void channelInactive(ChannelHandlerContext ctx) throws Exception
  {
    String channelId = ctx.channel().id().asShortText();
    String localIpAddress = ((InetSocketAddress) ctx.channel().localAddress()).getAddress().getHostAddress();

    ClientSocketChannelMap.remove(index);
    log.info("[client][" + channelId + "][" + localIpAddress + "]连接已关闭。");

    consoleArea.append(now() + "[client][" + index + "][" + channelId + "]连接已关闭。" + lineWrap);
  }

  @Override
  public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception
  {
    if (evt instanceof IdleStateEvent)
    {
      String channelId = ctx.channel().id().asShortText();
      String localIpAddress = ((InetSocketAddress) ctx.channel().localAddress()).getAddress().getHostAddress();

      IdleStateEvent e = (IdleStateEvent) evt;
      // 断开连接
      ctx.channel().close().sync();
      switch (e.state())
      {
        case READER_IDLE:
          log.info("[client][" + channelId + "][" + localIpAddress + "]" + "读取网络连接超时，连接已断开。");
          consoleArea.append(now() + "[client][" + index + "][" + channelId + "]读取网络连接超时，连接已断开。" + lineWrap);
          break;
        case WRITER_IDLE:
          log.info("[client][" + channelId + "][" + localIpAddress + "]" + "写入网络连接超时，连接已断开。");
          consoleArea.append(now() + "[client][" + index + "][" + channelId + "]写入网络连接超时，连接已断开。" + lineWrap);
          break;
        case ALL_IDLE:
          log.info("[client][" + channelId + "][" + localIpAddress + "]" + "读写网络连接超时，连接已断开。");
          consoleArea.append(now() + "[client][" + index + "][" + channelId + "]读写网络连接超时，连接已断开。" + lineWrap);
          break;
        default:
          break;
      }
    }
  }

  /**
   * 发送数据包
   * 
   * @param ctx
   * @param packet
   */
  private void sendPacket(Integer index, Channel channel, BasePacket packet)
  {
    String channelId = channel.id().asShortText();
    String remoteIpAddress = ((InetSocketAddress) channel.remoteAddress()).getAddress().getHostAddress();
    String logHeader = "[client][" + channelId + "][" + remoteIpAddress + "]";

    channel.writeAndFlush(packet).addListener(new ChannelFutureListener()
    {
      // 数据发送完毕后，触发本事件
      @Override
      public void operationComplete(ChannelFuture future) throws Exception
      {
        // log.debug(logHeader + packet.getPacketName() + "发送数据成功。");
        // consoleArea.append(now() + "[client][" + index + "][" + channelId +
        // "]发送数据成功。数据内容：" + packet.toString() + lineWrap);
      }
    });
  }
}
